package com.jingge.autojob.skeleton.model.builder;

import com.jingge.autojob.skeleton.cluster.balancing.LoadBalancingStrategy;
import com.jingge.autojob.skeleton.enumerate.LoadBalancingEnum;
import com.jingge.autojob.skeleton.enumerate.RemoteProtocolType;
import com.jingge.autojob.skeleton.enumerate.SaveStrategy;
import com.jingge.autojob.skeleton.enumerate.SchedulingStrategy;
import com.jingge.autojob.skeleton.framework.boot.AutoJobApplication;
import com.jingge.autojob.skeleton.framework.config.AutoJobMailConfig;
import com.jingge.autojob.skeleton.framework.config.AutoJobRetryConfig;
import com.jingge.autojob.skeleton.framework.config.AutoJobShardingConfig;
import com.jingge.autojob.skeleton.framework.mail.IMailClient;
import com.jingge.autojob.skeleton.framework.mail.MailClientFactory;
import com.jingge.autojob.skeleton.framework.task.AutoJobTask;
import com.jingge.autojob.skeleton.framework.task.AutoJobTrigger;
import com.jingge.autojob.skeleton.framework.task.NumericalShardingStrategy;
import com.jingge.autojob.skeleton.framework.task.ShardingStrategy;
import com.jingge.autojob.skeleton.model.task.remote.RemoteTask;
import com.jingge.autojob.skeleton.model.task.remote.BaseJsonHttpInterceptor;
import com.jingge.autojob.util.convert.DefaultValueUtil;
import com.jingge.autojob.util.id.IdGenerator;
import okhttp3.HttpUrl;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 远程任务构建者
 *
 * @author JingGe(* ^ ▽ ^ *)
 * @date 2023-09-05 15:53
 * @email 1158055613@qq.com
 */
public class AutoJobRemoteTaskBuilder {
    /**
     * 任务ID，该ID将会作为调度的唯一键
     */
    private long taskId;
    private Long versionID;
    /**
     * 任务类型，默认是内存型任务
     */
    private AutoJobTask.TaskType taskType;


    /**
     * 建立连接超时时间
     */
    private long connectTimeout;

    /**
     * 获取数据的超时时间
     */
    private long readTimeout;

    /**
     * 写入数据的超时时间
     */
    private long writeTimeout;
    /**
     * 任务别名
     */
    private String taskAlias;

    /**
     * 任务级别
     */
    private int taskLevel;
    /**
     * 触发器
     */
    private AutoJobTrigger trigger;

    /**
     * 所属
     */
    private Long belongTo;
    /**
     * 分片策略
     */
    private ShardingStrategy shardingStrategy;

    /**
     * 分片配置
     */
    private AutoJobShardingConfig shardingConfig;

    /**
     * 保存策略
     */
    private SaveStrategy saveStrategy;

    /**
     * 调度策略
     */
    private SchedulingStrategy schedulingStrategy;

    /**
     * 是否是子任务
     */
    private boolean isChildTask;

    /**
     * 重试配置
     */
    private AutoJobRetryConfig retryConfig;

    /**
     * 邮件配置
     */
    private AutoJobMailConfig mailConfig;

    /**
     * 是否自动保存
     */
    private boolean isSave;

    /**
     * 负载均衡策略
     */
    private LoadBalancingEnum loadBalancingStrategy;

    private IMailClient mailClient;

    private final List<String> executableMachines = new LinkedList<>();

    public AutoJobRemoteTaskBuilder() {
        this(IdGenerator.getNextIdAsLong());
    }

    public AutoJobRemoteTaskBuilder(long taskId) {
        this.taskId = taskId;
        trigger = AutoJobTriggerFactory.newDelayTrigger((long) (AutoJobApplication
                .getInstance()
                .getConfigHolder()
                .getAutoJobConfig()
                .getAnnotationDefaultDelayTime() * 60 * 1000), TimeUnit.MILLISECONDS);
        taskType = AutoJobTask.TaskType.MEMORY_TASk;
        saveStrategy = SaveStrategy.SAVE_IF_ABSENT;
        schedulingStrategy = SchedulingStrategy.JOIN_SCHEDULING;
        connectTimeout = 10000;
        readTimeout = 30000;
        writeTimeout = 30000;
        loadBalancingStrategy= LoadBalancingEnum.POLLING;
    }

    /**
     * 添加一个简单触发器，添加多个触发器时前者将被后者覆盖
     *
     * @param firstTriggeringTime 首次触发时间
     * @param repeatTimes         重复次数，任务总触发次数=1+repeatTimes，-1表示无限次触发
     * @param cycle               周期
     * @param unit                时间单位
     * @return com.example.autojob.skeleton.model.builder.AutoJobRemoteTaskBuilder
     * @author Huang Yongxiang
     * @date 2022/10/27 9:59
     */
    public AutoJobRemoteTaskBuilder addASimpleTrigger(long firstTriggeringTime, int repeatTimes, long cycle, TimeUnit unit) {
        this.trigger = AutoJobTriggerFactory
                .newSimpleTrigger(firstTriggeringTime, repeatTimes, cycle, unit)
                .setTaskId(taskId);
        return this;
    }

    /**
     * 添加一个cron-like表达式的触发器，添加多个触发器时前者将被后者覆盖
     *
     * @param cronExpression cron-like表达式
     * @param repeatTimes    重复次数，任务总触发次数=1+repeatTimes，-1表示无限次触发
     * @return com.example.autojob.skeleton.model.builder.AutoJobRemoteTaskBuilder
     * @author Huang Yongxiang
     * @date 2022/10/27 10:03
     */
    public AutoJobRemoteTaskBuilder addACronExpressionTrigger(String cronExpression, int repeatTimes) {
        this.trigger = AutoJobTriggerFactory
                .newCronExpressionTrigger(cronExpression, repeatTimes)
                .setTaskId(taskId);
        return this;
    }

    /**
     * 添加一个子任务触发器，该任务将会作为一个子任务参与调度，添加多个触发器时前者将被后者覆盖
     *
     * @return com.example.autojob.skeleton.model.builder.AutoJobRemoteTaskBuilder
     * @author Huang Yongxiang
     * @date 2022/10/27 10:09
     */
    public AutoJobRemoteTaskBuilder addAChildTaskTrigger() {
        this.trigger = AutoJobTriggerFactory
                .newChildTrigger()
                .setTaskId(taskId);
        schedulingStrategy = SchedulingStrategy.AS_CHILD_TASK;
        isChildTask = true;
        return this;
    }

    /**
     * 添加一个延迟触发器，任务将会在给定延迟后触发一次，添加多个触发器时前者将被后者覆盖
     *
     * @param delay 距离现在延迟执行的时间
     * @param unit  时间单位
     * @return com.example.autojob.skeleton.model.builder.AutoJobRemoteTaskBuilder
     * @author Huang Yongxiang
     * @date 2022/10/27 10:12
     */
    public AutoJobRemoteTaskBuilder addADelayTrigger(long delay, TimeUnit unit) {
        this.trigger = AutoJobTriggerFactory
                .newDelayTrigger(delay, unit)
                .setTaskId(taskId);
        schedulingStrategy = SchedulingStrategy.DELAY_SCHEDULE;
        return this;
    }

    public AutoJobRemoteTaskBuilder setTrigger(AutoJobTrigger trigger) {
        this.trigger = trigger;
        return this;
    }

    public AutoJobRemoteTaskBuilder addExecutableMachine(String machineAddress) {
        this.executableMachines.add(machineAddress);
        return this;
    }

    public AutoJobRemoteTaskBuilder addAllExecutableMachine(List<String> machineAddress) {
        this.executableMachines.addAll(machineAddress);
        return this;
    }

    public AutoJobRemoteTaskBuilder setMailConfig(AutoJobMailConfig mailConfig) {
        this.mailConfig = mailConfig;
        if (mailConfig != null) {
            mailClient = MailClientFactory.createMailClient(mailConfig);
        }
        return this;
    }

    public AutoJobRemoteTaskBuilder setTaskId(long taskId) {
        this.taskId = taskId;
        return this;
    }

    public AutoJobRemoteTaskBuilder setTaskType(AutoJobTask.TaskType taskType) {
        this.taskType = taskType;
        return this;
    }

    public AutoJobRemoteTaskBuilder setTaskAlias(String taskAlias) {
        this.taskAlias = taskAlias;
        return this;
    }

    public AutoJobRemoteTaskBuilder setTaskLevel(int taskLevel) {
        this.taskLevel = taskLevel;
        return this;
    }

    public AutoJobRemoteTaskBuilder setBelongTo(Long belongTo) {
        this.belongTo = belongTo;
        return this;
    }

    public AutoJobRemoteTaskBuilder setShardingStrategy(ShardingStrategy shardingStrategy) {
        this.shardingStrategy = shardingStrategy;
        return this;
    }

    public AutoJobRemoteTaskBuilder setShardingConfig(boolean enable, Object total) {
        this.shardingConfig = new AutoJobShardingConfig(null, enable, total);
        return this;
    }

    public AutoJobRemoteTaskBuilder setShardingConfig(AutoJobShardingConfig shardingConfig) {
        this.shardingConfig = shardingConfig;
        return this;
    }

    public AutoJobRemoteTaskBuilder setRetryConfig(AutoJobRetryConfig retryConfig) {
        this.retryConfig = retryConfig;
        return this;
    }

    public AutoJobRemoteTaskBuilder setChildTask(boolean childTask) {
        isChildTask = childTask;
        return this;
    }

    public AutoJobRemoteTaskBuilder setSaveStrategy(SaveStrategy saveStrategy) {
        this.saveStrategy = saveStrategy;
        return this;
    }

    public AutoJobRemoteTaskBuilder setSave(boolean save) {
        isSave = save;
        return this;
    }

    public AutoJobRemoteTaskBuilder setVersionID(Long versionID) {
        this.versionID = versionID;
        return this;
    }

    public AutoJobRemoteTaskBuilder setConnectTimeout(long connectTimeout, TimeUnit unit) {
        this.connectTimeout = unit.toMillis(connectTimeout);
        return this;
    }

    public AutoJobRemoteTaskBuilder setReadTimeout(long readTimeout, TimeUnit unit) {
        this.readTimeout = unit.toMillis(readTimeout);
        return this;
    }

    public AutoJobRemoteTaskBuilder setWriteTimeout(long writeTimeout, TimeUnit unit) {
        this.writeTimeout = unit.toMillis(writeTimeout);
        return this;
    }

    public AutoJobRemoteTaskBuilder setMailClient(IMailClient mailClient) {
        this.mailClient = mailClient;
        return this;
    }

    public AutoJobRemoteTaskBuilder setLoadBalancingStrategy(LoadBalancingEnum loadBalancingEnum) {
        this.loadBalancingStrategy = loadBalancingEnum;
        return this;
    }


    /**
     * 创建一个基于HTTP协议的远程任务
     *
     * @param url         完整的URL地址
     * @param method      请求类型
     * @param headers     请求头，不需要置null
     * @param queryParams 请求参数，不需要置null
     * @param body        请求体，默认使用JSON序列化，不需要置为null
     * @return com.jingge.autojob.skeleton.model.task.remote.RemoteTask
     * @author JingGe(* ^ ▽ ^ *)
     * @date 2023/9/12 14:08
     */
    public RemoteTask createAsHttpTask(String url, String method, Map<String, List<String>> headers, Map<String, Object> queryParams, Object body) {
        RemoteTask task = new RemoteTask();
        HttpUrl uri = HttpUrl.parse(url);
        if (uri == null) {
            throw new IllegalArgumentException("不合法的URL");
        }
        task.setHost(uri.host());
        task.setPort(uri.port());
        task.setUri(uri
                .uri()
                .getPath());
        task.setConnectTimeout(connectTimeout);
        task.setReadTimeout(readTimeout);
        task.setWriteTimeout(writeTimeout);
        task.setProtocolType(RemoteProtocolType.findByType(uri.scheme()));
        task.setInterceptor(new BaseJsonHttpInterceptor());
        task.setHeader(headers);
        task.setBody(body);
        task.setReqType(method.toUpperCase());
        if (shardingConfig != null && shardingConfig.isEnable()) {
            queryParams = queryParams == null ? new HashMap<>() : queryParams;
            queryParams.put("totalSharding", shardingConfig.getTotal());
        }
        task.setParams(new Object[]{queryParams});
        task.setParamsString(AttributesBuilder
                .getInstance()
                .addParams(HashMap.class, queryParams)
                .getAttributesString());
        build(task);
        return task;
    }

    private void build(RemoteTask remoteTask) {
        remoteTask.setId(taskId);
        if (trigger.getTaskId() == null) {
            trigger.setTaskId(taskId);
        }
        remoteTask.setTrigger(trigger);
        remoteTask.setVersionId(versionID);
        remoteTask.setAlias(taskAlias);
        remoteTask.setIsChildTask(isChildTask);
        remoteTask.setBelongTo(belongTo);
        remoteTask.setSaveStrategy(saveStrategy);
        remoteTask.setLoadBalancing(loadBalancingStrategy);
        remoteTask.setSchedulingStrategy(schedulingStrategy);
        remoteTask.setMailConfig(mailConfig);
        remoteTask.setIsChildTask(isChildTask);
        remoteTask.setExecutableMachines(executableMachines);
        if (shardingConfig != null) {
            if (shardingConfig.getTaskId() == null) {
                shardingConfig.setTaskID(taskId);
            }
            remoteTask.setShardingStrategy(DefaultValueUtil.defaultValue(shardingStrategy, new NumericalShardingStrategy()));
            remoteTask.setShardingConfig(shardingConfig);
        }
        if (mailConfig != null && mailConfig.getTaskId() == null) {
            mailConfig.setTaskId(taskId);
        }
        remoteTask.setRetryConfig(DefaultValueUtil.defaultValue(retryConfig, new AutoJobRetryConfig().setTaskId(remoteTask.getId())));
        remoteTask.setMailClient(DefaultValueUtil.defaultValue(mailClient, AutoJobApplication
                .getInstance()
                .getMailClient()));
        remoteTask.setType(taskType);
        remoteTask.setTaskLevel(taskLevel);
        if (isSave) {
            remoteTask.save();
        }
    }


}
